
<!DOCTYPE html>
<html lang="en" class="loading">
<head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Springboot整合RabbitMp - kyralo</title>
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="google" content="notranslate">
    <meta name="keywords" content="kyralo,"> 
    <meta name="description" content="RabbitMQ即一个消息队列，主要是用来实现应用程序的异步和解耦，同时也能起到消息缓冲，消息分发的作用。
消息中间件在互联网公司的使用中越来越多，刚才还看到新闻阿里将RocketMQ捐献给了apa,"> 
    <meta name="author" content="kyralo"> 
    
    <link rel="icon" href="https://cdn.jsdelivr.net/gh/kyralo/cdn/ico/favicon.ico"> 
    <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kyralo/cdn@v1.0.2/css/diaspora.css">
    <!-- <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> -->
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({
              google_ad_client: "ca-pub-8691406134231910",
              enable_page_level_ads: true
         });
    </script>
    <!-- <script async custom-element="amp-auto-ads"
        src="https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js">
    </script> -->
</head>
</html>
<body class="loading">
    <span id="config-title" style="display:none">kyralo</span>
    <div id="loader"></div>
    <div id="single">
    <div id="top" style="display: block;">
    <div class="bar" style="width: 0;"></div>
    <a class="icon-home image-icon" href="javascript:;" data-url="https://kyralo.github.io"></a>
    <!-- <div title="播放/暂停" class="icon-play"></div> -->
    <h3 class="subtitle">Springboot整合RabbitMp</h3>
    <div class="social">
        <div>
            <div class="share">
                <a title="获取二维码" class="icon-scan" href="javascript:;"></a>
            </div>
            <div id="qr"></div>
        </div>
    </div>
    <div class="scrollbar"></div>
</div>

    <div class="section">
        <div class="article">
    <div class='main'>
        <h1 class="title">Springboot整合RabbitMp</h1>
        <div class="stuff">
            <span>八月 10, 2019</span>
            
  <ul class="post-tags-list"><li class="post-tags-list-item"><a class="post-tags-list-link" href="/tags/Java/">Java</a></li><li class="post-tags-list-item"><a class="post-tags-list-link" href="/tags/RabbitMQ/">RabbitMQ</a></li><li class="post-tags-list-item"><a class="post-tags-list-link" href="/tags/Springboot/">Springboot</a></li><li class="post-tags-list-item"><a class="post-tags-list-link" href="/tags/微服务/">微服务</a></li></ul>


        </div>
        <div class="content markdown">
            <h2 id="RabbitMQ"><a href="#RabbitMQ" class="headerlink" title="RabbitMQ"></a>RabbitMQ</h2><p>即一个消息队列，主要是用来实现应用程序的异步和解耦，同时也能起到消息缓冲，消息分发的作用。</p>
<p>消息中间件在互联网公司的使用中越来越多，刚才还看到新闻阿里将RocketMQ捐献给了apache，当然了今天的主角还是讲RabbitMQ。消息中间件最主要的作用是解耦，中间件最标准的用法是生产者生产消息传送到队列，消费者从队列中拿取消息并处理，生产者不用关心是谁来消费，消费者不用关心谁在生产消息，从而达到解耦的目的。在分布式的系统中，消息队列也会被用在很多其它的方面，比如：分布式事务的支持，RPC的调用等等。</p>
<p>以前一直使用的是ActiveMQ，在实际的生产使用中也出现了一些小问题，在网络查阅了很多的资料后，决定尝试使用RabbitMQ来替换ActiveMQ，RabbitMQ的高可用性、高性能、灵活性等一些特点吸引了我们，查阅了一些资料整理出此文。</p>
<h3 id="RabbitMQ介绍"><a href="#RabbitMQ介绍" class="headerlink" title="RabbitMQ介绍"></a>RabbitMQ介绍</h3><p>RabbitMQ是实现AMQP（高级消息队列协议）的消息中间件的一种，最初起源于金融系统，用于在分布式系统中存储转发消息，在易用性、扩展性、高可用性等方面表现不俗。RabbitMQ主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时，消费者无法快速消费，那么需要一个中间层。保存这个数据。</p>
<p>AMQP，即Advanced Message Queuing Protocol，高级消息队列协议，是应用层协议的一个开放标准，为面向消息的中间件设计。消息中间件主要用于组件之间的解耦，消息的发送者无需知道消息使用者的存在，反之亦然。AMQP的主要特征是面向消息、队列、路由（包括点对点和发布/订阅）、可靠性、安全。</p>
<p>RabbitMQ是一个开源的AMQP实现，服务器端用Erlang语言编写，支持多种客户端，如：Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等，支持AJAX。用于在分布式系统中存储转发消息，在易用性、扩展性、高可用性等方面表现不俗。</p>
<h3 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h3><p>通常我们谈到队列服务, 会有三个概念： 发消息者、队列、收消息者，RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。</p>
<p><img src="https://github.com/haoxiaoyong1014/springboot-examples/raw/master/springboot-rabbitmq/src/main/java/com/hxy/rabbitmq/img/v2.jpg" alt="image"></p>
<p>左侧 P 代表 生产者，也就是往 RabbitMQ 发消息的程序。<br>中间即是 RabbitMQ，其中包括了 交换机 和 队列。<br>右侧 C 代表 消费者，也就是往 RabbitMQ 拿消息的程序。<br>那么，其中比较重要的概念有 4 个，分别为：虚拟主机，交换机，队列，和绑定。</p>
<p>虚拟主机：一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢？很简单，RabbitMQ当中，用户只能在虚拟主机的粒度进行权限控制。 因此，如果需要禁止A组访问B组的交换机/队列/绑定，必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。<br>交换机：Exchange 用于转发消息，但是它不会做存储 ，如果没有 Queue bind 到 Exchange 的话，它会直接丢弃掉 Producer 发送过来的消息。 这里有一个比较重要的概念：路由键 。消息到交换机的时候，交互机会转发到对应的队列中，那么究竟转发到哪个队列，就要根据该路由键。<br>绑定：也就是交换机需要和队列相绑定，这其中如上图所示，是多对多的关系。<br>交换机(Exchange)<br>交换机的功能主要是接收消息并且转发到绑定的队列，交换机不存储消息，在启用ack模式后，交换机找不到队列会返回错误。交换机有四种类型：Direct, topic, Headers and Fanout</p>
<p>Direct：direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的 routing_key 匹配时, 才会被交换器投送到绑定的队列中去.<br>Topic：按规则转发消息（最灵活）<br>Headers：设置header attribute参数类型的交换机<br>Fanout：转发消息到所有绑定队列<br>Direct Exchange<br>Direct Exchange是RabbitMQ默认的交换机模式，也是最简单的模式，根据key全文匹配去寻找队列。</p>
<p><img src="https://github.com/haoxiaoyong1014/springboot-examples/raw/master/springboot-rabbitmq/src/main/java/com/hxy/rabbitmq/img/v3.jpg" alt="image"></p>
<p>第一个 X - Q1 就有一个 binding key，名字为 orange； X - Q2 就有 2 个 binding key，名字为 black 和 green。当消息中的 路由键 和 这个 binding key 对应上的时候，那么就知道了该消息去到哪一个队列中。</p>
<p>Ps：为什么 X 到 Q2 要有 black，green，2个 binding key呢，一个不就行了吗？ - 这个主要是因为可能又有 Q3，而Q3只接受 black 的信息，而Q2不仅接受black 的信息，还接受 green 的信息。</p>
<h3 id="Topic-Exchange"><a href="#Topic-Exchange" class="headerlink" title="Topic Exchange"></a>Topic Exchange</h3><p>Topic Exchange 转发消息主要是根据通配符。 在这种交换机下，队列和交换机的绑定会定义一种路由模式，那么，通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息。</p>
<p>在这种交换机模式下：</p>
<p>路由键必须是一串字符，用句号（.） 隔开，比如说 <a href="http://agreements.us，或者" target="_blank" rel="noopener">http://agreements.us，或者</a> agreements.eu.stockholm 等。<br>路由模式必须包含一个 星号（<em>），主要用于匹配路由键指定位置的一个单词，比如说，一个路由模式是这样子：agreements..b.</em>，那么就只能匹配路由键是这样子的：第一个单词是 agreements，第四个单词是 b。 井号（#）就表示相当于一个或者多个单词，例如一个匹配模式是agreements.eu.berlin.#，那么，以agreements.eu.berlin开头的路由键都是可以的。<br>具体代码发送的时候还是一样，第一个参数表示交换机，第二个参数表示routing key，第三个参数即消息。如下：<br>rabbitTemplate.convertAndSend(“testTopicExchange”,”key1.a.c.key2”, “ this is  RabbitMQ!”);<br>topic 和 direct 类似, 只是匹配上支持了”模式”, 在”点分”的 routing_key 形式中, 可以使用两个通配符:</p>
<ul>
<li>表示一个词</li>
<li>表示零个或多个词<br>Headers Exchange<br>headers 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型. 在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列.</li>
</ul>
<p>Fanout Exchange</p>
<p>Fanout Exchange 消息广播的模式，不管路由键或者是路由模式，会把消息发给绑定给它的全部队列，如果配置了routing_key会被忽略。</p>
<p>springboot集成RabbitMQ<br>springboot集成RabbitMQ非常简单，如果只是简单的使用配置非常少，springboot提供了spring-boot-starter-amqp项目对消息各种支持。</p>
<h3 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h3><h4 id="1、配置pom包，主要是添加spring-boot-starter-amqp的支持"><a href="#1、配置pom包，主要是添加spring-boot-starter-amqp的支持" class="headerlink" title="1、配置pom包，主要是添加spring-boot-starter-amqp的支持"></a>1、配置pom包，主要是添加spring-boot-starter-amqp的支持</h4><pre><code>&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><h4 id="2、配置文件"><a href="#2、配置文件" class="headerlink" title="2、配置文件"></a>2、配置文件</h4><p>配置rabbitmq的安装地址、端口以及账户信息</p>
<pre><code>spring.application.name=spirng-boot-rabbitmq

spring.rabbitmq.host= 127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest</code></pre><h5 id="队列配置"><a href="#队列配置" class="headerlink" title="队列配置"></a>队列配置</h5><pre><code>@Configuration
public class RabbitConfig {

    @Bean
    public Queue Queue() {
        return new Queue(&quot;hello&quot;);
    }

}</code></pre><h4 id="发送者"><a href="#发送者" class="headerlink" title="发送者"></a>发送者</h4><p>rabbitTemplate是springboot 提供的默认实现</p>
<pre><code>public class HelloSender {

    @Autowired
    private AmqpTemplate rabbitTemplate;

    public void send() {
        String context = &quot;hello &quot; + new Date();
        System.out.println(&quot;Sender : &quot; + context);
        this.rabbitTemplate.convertAndSend(&quot;hello&quot;, context);
    }

}</code></pre><h4 id="接收者"><a href="#接收者" class="headerlink" title="接收者"></a>接收者</h4><pre><code>@Component
@RabbitListener(queues = &quot;hello&quot;)
public class HelloReceiver {

    @RabbitHandler
    public void process(String hello) {
        System.out.println(&quot;Receiver  : &quot; + hello);
    }

}</code></pre><h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><pre><code>@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqHelloTest {

    @Autowired
    private HelloSender helloSender;

    @Test
    public void hello() throws Exception {
        helloSender.send();
    }

}</code></pre><p>注意，发送者和接收者的queue name必须一致，不然不能接收<br>多对多使用</p>
<p>一个发送者，N个接收者或者N个发送者和N个接收者会出现什么情况呢？</p>
<h3 id="一对多发送"><a href="#一对多发送" class="headerlink" title="一对多发送"></a>一对多发送</h3><p>对上面的代码进行了小改造,接收端注册了两个Receiver,Receiver1和Receiver2，发送端加入参数计数，接收端打印接收到的参数，下面是测试代码，发送一百条消息，来观察两个接收端的执行效果。</p>
<pre><code>@Test
public void oneToMany() throws Exception {
    for (int i=0;i&lt;100;i++){
        neoSender.send(i);
    }
}</code></pre><p>结果如下：</p>
<p>Receiver 1: spirng boot neo queue <strong>**</strong> 11<br>Receiver 2: spirng boot neo queue <strong>**</strong> 12<br>Receiver 2: spirng boot neo queue <strong>**</strong> 14<br>Receiver 1: spirng boot neo queue <strong>**</strong> 13<br>Receiver 2: spirng boot neo queue <strong>**</strong> 15<br>Receiver 1: spirng boot neo queue <strong>**</strong> 16<br>Receiver 1: spirng boot neo queue <strong>**</strong> 18<br>Receiver 2: spirng boot neo queue <strong>**</strong> 17<br>Receiver 2: spirng boot neo queue <strong>**</strong> 19<br>Receiver 1: spirng boot neo queue <strong>**</strong> 20</p>
<p>根据返回结果得到以下结论 &gt; 一个发送者，N个接受者,经过测试会均匀的将消息发送到N个接收者中</p>
<h3 id="多对多发送"><a href="#多对多发送" class="headerlink" title="多对多发送"></a>多对多发送</h3><p>复制了一份发送者，加入标记，在一百个循环中相互交替发送</p>
<pre><code>@Test
    public void manyToMany() throws Exception {
        for (int i=0;i&lt;100;i++){
            neoSender.send(i);
            neoSender2.send(i);
        }
}</code></pre><p>结果如下：</p>
<p>Receiver 1: spirng boot neo queue <strong>**</strong> 20<br>Receiver 2: spirng boot neo queue <strong>**</strong> 20<br>Receiver 1: spirng boot neo queue <strong>**</strong> 21<br>Receiver 2: spirng boot neo queue <strong>**</strong> 21<br>Receiver 1: spirng boot neo queue <strong>**</strong> 22<br>Receiver 2: spirng boot neo queue <strong>**</strong> 22<br>Receiver 1: spirng boot neo queue <strong>**</strong> 23<br>Receiver 2: spirng boot neo queue <strong>**</strong> 23<br>Receiver 1: spirng boot neo queue <strong>**</strong> 24<br>Receiver 2: spirng boot neo queue <strong>**</strong> 24<br>Receiver 1: spirng boot neo queue <strong>**</strong> 25<br>Receiver 2: spirng boot neo queue <strong>**</strong> 25</p>
<p>结论：和一对多一样，接收端仍然会均匀接收到消息<br>高级使用</p>
<h3 id="对象的支持"><a href="#对象的支持" class="headerlink" title="对象的支持"></a>对象的支持</h3><p>springboot以及完美的支持对象的发送和接收，不需要格外的配置。</p>
<p>//发送者</p>
<pre><code>public void send(User user) {
    System.out.println(&quot;Sender object: &quot; + user.toString());
    this.rabbitTemplate.convertAndSend(&quot;object&quot;, user);
}</code></pre><p>…</p>
<p>//接受者</p>
<pre><code>@RabbitHandler
public void process(User user) {
    System.out.println(&quot;Receiver object : &quot; + user);
}</code></pre><p>结果如下：</p>
<p>Sender object: User{name=’neo’, pass=’123456’}<br>Receiver object : User{name=’neo’, pass=’123456’}</p>
<p>Topic Exchange</p>
<p>topic 是RabbitMQ中最灵活的一种方式，可以根据routing_key自由的绑定不同的队列</p>
<p>首先对topic规则配置，这里使用两个队列来测试</p>
<pre><code>@Configuration
public class TopicRabbitConfig {

    final static String message = &quot;topic.message&quot;;
    final static String messages = &quot;topic.messages&quot;;

    @Bean
    public Queue queueMessage() {
        return new Queue(TopicRabbitConfig.message);
    }

    @Bean
    public Queue queueMessages() {
        return new Queue(TopicRabbitConfig.messages);
    }

    @Bean
    TopicExchange exchange() {
        return new TopicExchange(&quot;exchange&quot;);
    }

    @Bean
    Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
        return BindingBuilder.bind(queueMessage).to(exchange).with(&quot;topic.message&quot;);
    }

    @Bean
    Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
        return BindingBuilder.bind(queueMessages).to(exchange).with(&quot;topic.#&quot;);
    }
}</code></pre><p>使用queueMessages同时匹配两个队列，queueMessage只匹配”topic.message”队列</p>
<pre><code>public void send1() {
    String context = &quot;hi, i am message 1&quot;;
    System.out.println(&quot;Sender : &quot; + context);
    this.rabbitTemplate.convertAndSend(&quot;exchange&quot;, &quot;topic.message&quot;, context);
}

public void send2() {
    String context = &quot;hi, i am messages 2&quot;;
    System.out.println(&quot;Sender : &quot; + context);
    this.rabbitTemplate.convertAndSend(&quot;exchange&quot;, &quot;topic.messages&quot;, context);
}</code></pre><p>发送send1会匹配到topic.#和topic.message 两个Receiver都可以收到消息，发送send2只有topic.#可以匹配所有只有Receiver2监听到消息</p>
<p>Fanout Exchange</p>
<p>Fanout 就是我们熟悉的广播模式或者订阅模式，给Fanout交换机发送消息，绑定了这个交换机的所有队列都收到这个消息。</p>
<p>Fanout 相关配置</p>
<pre><code>@Configuration
public class FanoutRabbitConfig {

    @Bean
    public Queue AMessage() {
        return new Queue(&quot;fanout.A&quot;);
    }

    @Bean
    public Queue BMessage() {
        return new Queue(&quot;fanout.B&quot;);
    }

    @Bean
    public Queue CMessage() {
        return new Queue(&quot;fanout.C&quot;);
    }

    @Bean
    FanoutExchange fanoutExchange() {
        return new FanoutExchange(&quot;fanoutExchange&quot;);
    }

    @Bean
    Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(AMessage).to(fanoutExchange);
    }

    @Bean
    Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(BMessage).to(fanoutExchange);
    }

    @Bean
    Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(CMessage).to(fanoutExchange);
    }

}</code></pre><p>这里使用了A、B、C三个队列绑定到Fanout交换机上面，发送端的routing_key写任何字符都会被忽略：</p>
<pre><code>public void send() {
        String context = &quot;hi, fanout msg &quot;;
        System.out.println(&quot;Sender : &quot; + context);
        this.rabbitTemplate.convertAndSend(&quot;fanoutExchange&quot;,&quot;&quot;, context);
}</code></pre><p>结果如下：</p>
<pre><code>&gt; Sender : hi, fanout msg 
...
fanout Receiver B: hi, fanout msg 
fanout Receiver A  : hi, fanout msg 
fanout Receiver C: hi, fanout msg </code></pre><p>结果说明，绑定到fanout交换机上面的队列都收到了消息</p>
<p><strong>消息持久化</strong></p>
<p>在生产环境中，我们需要考虑万一生产者挂了，消费者挂了，或者 rabbitmq 挂了怎么样。一般来说，如果生产者挂了或者消费者挂了，其实是没有影响，因为消息就在队列里面。那么万一 rabbitmq 挂了，之前在队列里面的消息怎么办，其实可以做消息持久化，RabbitMQ 会把信息保存在磁盘上。</p>
<p>做法是可以先从 Connection 对象中拿到一个 Channel 信道对象，然后再可以通过该对象设置 消息持久化。</p>
<p>生产者或者消费者断线重连<br>这里 Spring 有自动重连机制。</p>
<p><strong>ACK 确认机制</strong></p>
<p>每个Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中，Consumer出错了，异常退出了，而数据还没有处理完成，那么 非常不幸，这段数据就丢失了。因为我们采用no-ack的方式进行确认，也就是说，每次Consumer接到数据后，而不管是否处理完 成，RabbitMQ Server会立即把这个Message标记为完成，然后从queue中删除了。</p>
<p>如果一个Consumer异常退出了，它处理的数据能够被另外的Consumer处理，这样数据在这种情况下就不会丢失了（注意是这种情况下）。<br>为了保证数据不被丢失，RabbitMQ支持消息确认机制，即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到，那么我们不能采用no-ack。而应该是在处理完数据后发送ack。</p>
<p>在处理数据后发送的ack，就是告诉RabbitMQ数据已经被接收，处理完成，RabbitMQ可以去安全的删除它了。<br>如果Consumer退出了但是没有发送ack，那么RabbitMQ就会把这个Message发送到下一个Consumer。这样就保证了在Consumer异常退出的情况下数据也不会丢失。</p>
<p>如果对 ACK 机制有疑问点击这里:<a href="https://www.zhihu.com/question/41976893" target="_blank" rel="noopener">https://www.zhihu.com/question/41976893</a></p>

        </div>
        
    <div id='gitalk-container' class="comment link"
        data-ae='false'
        data-ci='eeec43124e53633945c0'
        data-cs='e2332cffb30ae24f185c1701286958cddd748ffb'
        data-r='kyralo.github.io'
        data-o='kyralo'
        data-a='kyralo'
        data-d='false'
    >查看评论</div>


    </div>
    
        <div class='side'>
            <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#RabbitMQ"><span class="toc-number">1.</span> <span class="toc-text">RabbitMQ</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ介绍"><span class="toc-number">1.1.</span> <span class="toc-text">RabbitMQ介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#相关概念"><span class="toc-number">1.2.</span> <span class="toc-text">相关概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Topic-Exchange"><span class="toc-number">1.3.</span> <span class="toc-text">Topic Exchange</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#简单使用"><span class="toc-number">1.4.</span> <span class="toc-text">简单使用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1、配置pom包，主要是添加spring-boot-starter-amqp的支持"><span class="toc-number">1.4.1.</span> <span class="toc-text">1、配置pom包，主要是添加spring-boot-starter-amqp的支持</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2、配置文件"><span class="toc-number">1.4.2.</span> <span class="toc-text">2、配置文件</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#队列配置"><span class="toc-number">1.4.2.1.</span> <span class="toc-text">队列配置</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#发送者"><span class="toc-number">1.4.3.</span> <span class="toc-text">发送者</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接收者"><span class="toc-number">1.4.4.</span> <span class="toc-text">接收者</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#测试"><span class="toc-number">1.4.5.</span> <span class="toc-text">测试</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#一对多发送"><span class="toc-number">1.5.</span> <span class="toc-text">一对多发送</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#多对多发送"><span class="toc-number">1.6.</span> <span class="toc-text">多对多发送</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#对象的支持"><span class="toc-number">1.7.</span> <span class="toc-text">对象的支持</span></a></li></ol></li></ol>
        </div>
    
</div>


    </div>
</div>
</body>
<script src="//cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script src="//lib.baomitu.com/jquery/1.8.3/jquery.min.js"></script>
<script src="//cdn.jsdelivr.net/gh/kyralo/cdn/js/plugin.js"></script>
<script src="//cdn.jsdelivr.net/gh/kyralo/cdn/js/diaspora.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/kyralo/cdn/css/photoswipe.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/kyralo/cdn/css/default-skin.css">
<script src="//cdn.jsdelivr.net/gh/kyralo/cdn/js/photoswipe.min.js"></script>
<script src="//cdn.jsdelivr.net/gh/kyralo/cdn/js/photoswipe-ui-default.min.js"></script>

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>
    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">
        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>
        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">
            <div class="pswp__top-bar">
                <!--  Controls are self-explanatory. Order can be changed. -->
                <div class="pswp__counter"></div>
                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
                <button class="pswp__button pswp__button--share" title="Share"></button>
                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>
            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>
            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>
            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>
            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>
        </div>
    </div>
</div>



<!-- Google Analytics -->
<script type="text/javascript">
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-146789315-1', 'auto');
  ga('send', 'pageview');
</script>
<!-- End Google Analytics -->


<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.css">
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.js"></script>
<style>
  .aplayer .aplayer-lrc {
    height: 35px;
  }
  .aplayer .aplayer-lrc p{
    font-size: 16px;
    font-weight: 700;
    line-height: 18px !important;
  }
  .aplayer .aplayer-lrc p.aplayer-lrc-current{
    color: #FF1493;
  }
  .aplayer.aplayer-narrow .aplayer-body{
    left: -66px !important;
  }
  .aplayer.aplayer-fixed .aplayer-lrc {
    display: none;
  }
  .aplayer .aplayer-lrc.aplayer-lrc-hide {
      display:none !important;
  }
  .aplayer.aplayer-fixed .lrc-show {
    display: block;
    background: rgba(255, 255, 255, 0.8);
  }
</style>
<div class="aplayer"

    data-id="2725346449"

    data-server="netease"

    data-type="playlist"

    data-fixed="true"

    data-autoplay="false"

    data-loop="all"

    data-order="random"

    data-preload="auto"

    data-volume="0.7"

    data-mutex="true"

</div>
<script src="https://cdn.jsdelivr.net/npm/meting@1.2/dist/Meting.min.js"></script>
<script>
  $(function(){
    $('body').on('click', '.aplayer', function(){
      if($('.aplayer-button').hasClass('aplayer-play')) {
        $('.aplayer-lrc').removeClass('lrc-show');
      } else {
        $('.aplayer-lrc').addClass('lrc-show');
      }
    })
  });
</script>
</html>
